package os;

import java.util.ArrayList;
import java.util.List;

/**
 * Super class of any thread of execution in the {@link os.MiniOS}.
 * 
 * @author Ahmed El-Eryan
 */
public abstract class MiniThread extends Thread
{

	/**
	 * Constant to indicate that the thread in the <i>Running</i> state.
	 */
	public static final int THREAD_RUNNING = 0;

	/**
	 * Constant to indicate that the thread in the <i>Ready</i> state.
	 */
	public static final int THREAD_READY = 1;

	/**
	 * Constant to indicate that the thread in the <i>Blocked</i> state.
	 */
	public static final int THREAD_BLOCKED = 2;

	/**
	 * Constant to indicate that the thread in the <i>Suspended Ready</i> state.
	 */
	public static final int THREAD_SUSPENDED_READY = 3;

	/**
	 * Constant to indicate that the thread in the <i>Suspended Blocked</i>
	 * state.
	 */
	public static final int THREAD_SUSPENDED_BLOCKED = 4;

	/**
	 * String literals representing different states of the threads.
	 */
	public static final String[] THREAD_STATES = { "Running", "Ready",
			"Blocked", "Suspended Ready", "Suspneded Blocked" };

	/**
	 * Id of the thread.
	 */
	protected String tid;

	/**
	 * Process owning this thread.
	 */
	protected MiniProcess process;

	/**
	 * State of the thread. It can be any of {@link #THREAD_RUNNING},
	 * {@link #THREAD_READY}, {@link #THREAD_BLOCKED},
	 * {@link #THREAD_SUSPENDED_READY}, or {@link #THREAD_SUSPENDED_BLOCKED}.
	 */
	protected int threadState = THREAD_READY;

	/**
	 * Description of the thread. Shown in the Process Monitor tab.
	 */
	protected String description;
	/**
	 * indicate state of the thread stopping or running
	 */
	protected boolean stopped;
	
	/**
	 * The last input received from the O.S.
	 */
	protected String lastInput;

	/**
	 * The priority of the thread
	 */
	protected int threadPriority;
	
	/**
	 * The remaining time of the thread
	 */
	protected int remainingTime;
	
	/**
	 * The threads joining this thread.
	 * will be notified when this thread dies.
	 */
	protected List<MiniThread> joinedThreads;

	protected MiniMutex block;
	/**
	 * Constructor of MiniThread.
	 * 
	 * @param process
	 *            Owning process.
	 * @param tid
	 *            id of the thread.
	 */
	public MiniThread(MiniProcess process, String tid)
	{
		this.process = process;
		this.tid = tid;
		this.stopped = true;
		this.block = new MiniMutex(1);
		joinedThreads = new ArrayList<MiniThread>();
	}

	
	public int getThreadPriority()
	{
		return threadPriority;
	}


	public void setThreadPriority(int threadPriority)
	{
		this.threadPriority = threadPriority;
	}


	public int getRemainingTime()
	{
		return remainingTime;
	}


	public void setRemainingTime(int remainingTime)
	{
		this.remainingTime = remainingTime;
	}


	/**
	 * In case this is the main thread of execution of a process, this method is
	 * called to pass the console arguments supplied by the user to the thread.
	 * 
	 * @param arguments
	 *            Console arguments supplied by the user when executing the
	 *            process.
	 */
	public abstract void passThreadArguments(Object[] arguments);

	/**
	 * Code executed by the thread is written in this method.
	 */
	public abstract void run();

	/**
	 * <b>UnImplemented</b>: Invoked by the
	 * {@link os.kernelProcess.SchedulerThread} to resume the execution of this
	 * thread.
	 */
	// tested
	public synchronized void resumeExecution()
	{
		// XXX Done MiniThread::resumeExecution
		stopped = false;
		// System.out.println("Thread no "+tid+" is resuming execution");
		notifyAll();
	}

	/**
	 * <b>UnImplemented</b>: Invoked by the
	 * {@link os.kernelProcess.SchedulerThread} to stop the execution of this
	 * thread.
	 */
	// tested
	public void stopExecution()
	{
		//XXX Done MiniThread::stopExecution
		// System.out.println("Thread no "+tid+" is stopping execution");
		// MiniOS.getInstance().getView().displayMessage("Thread no "+tid+" is stopping execution");
		stopped = true;
	}

	/**
	 * <b>UnImplemented</b>: Invoked by the thread to check whether the
	 * {@link os.kernelProcess.SchedulerThread} has stopped the thread or not.
	 */
	// tested
	public synchronized void isExcecutionStopped()
	{
		//XXX Done MiniThread::isExcecutionStopped
		try
		{
//			System.out.println("Thread no "+tid+" is checking for execution stop");
//			MiniOS.getInstance().getView().displayMessage("Thread no "+tid+" is checking for execution stop");
			while (stopped)
			{
				wait();
//				System.out.println("Thread no "+tid+" is notified");
			}
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
//		System.out.println("Thread no "+tid+" is continue execution");
//		MiniOS.getInstance().getView().displayMessage("Thread no "+tid+" is continue execution");
	}

	/**
	 * <b>UnImplemented</b>: Invoked by the thread to get the result of the
	 * input request when the request if fulfilled.
	 * 
	 * @return Input of the user.
	 */
	public String getInput()
	{
		// XXX: Done MiniThread:getInput()
		return lastInput;
	}

	/**
	 * Getter of thread id.
	 * 
	 * @return Thread id.
	 */
	public String getTid()
	{
		return tid;
	}

	/**
	 * Getter of thread's owning process.
	 * 
	 * @return Thread's owning process.
	 */
	public MiniProcess getProcess()
	{
		return process;
	}

	/**
	 * Shortcut for thread's owning process id.
	 * 
	 * @return Thread's owning process id.
	 */
	public String getProcessId()
	{
		return process.getPid();
	}

	/**
	 * Setter of thread state.
	 * 
	 * @param state
	 *            New state.
	 */
	public void setThreadState(int state)
	{
		this.block.P();
			this.threadState = state;
		this.block.V();
	}

	/**
	 * Getter of thread state literal.
	 * 
	 * @return Thread state literal.
	 */
	public String getThreadStateLiteral()
	{
		String threadStateTemp = null;
		this.block.P();
			threadStateTemp = THREAD_STATES[threadState];
		this.block.V();
		return threadStateTemp;
	}

	/**
	 * Getter of thread state.
	 * 
	 * @return Thread state.
	 */
	public int getThreadState()
	{
		return threadState;
	}

	/**
	 * Getter of thread description.
	 * 
	 * @return Thread description.
	 */
	public String getDescription()
	{
		return description;
	}

	public void setInput(String lastInput)
	{
		this.lastInput = lastInput;
	}

	/**
	 * Add a joining thread
	 * @param parentThread
	 */
	public void addJoin(MiniThread parentThread)
	{
		joinedThreads.add(parentThread);
	}

	/**
	 * Called when the thread is terminated to resume joined threads
	 */
	public void resumeWaiting()
	{
		for (int i = 0, n = joinedThreads.size(); i < n; i++)
		{
			MiniOS.getInstance().unblockThread(joinedThreads.get(i));
		}
	}
}
